﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.Foundation;

namespace Emilie.Animation.Simple
{
    public static class Transform
    {

        #region TRANSLATE ANIMATIONS

        /// <summary>
        /// Adds two translation DoubleAnimations to a Storyboard - one for a X Axis and one for the Y Axis.
        /// </summary>
        /// <param name="storyboard"></param>
        /// <param name="target">The FrameworkElement to animate across both Axis</param>
        /// <param name="_fromX">Optional starting X Axis translation. Leave null to use current.</param>
        /// <param name="_fromY">Optional starting Y Axis translation. Leave null to use current.</param>
        /// <param name="_toX">Final X Axis Translation</param>
        /// <param name="_toY">Final X Axis Translation</param>
        /// <param name="beginTime">When to start the double animations in the storyboards timeline</param>
        /// <param name="_duration">The duration of the translation animation</param>
        /// <param name="_easingMode">EasingFunction to apply to the translations</param>
        public static void AddDoubleAnimationTranslate(this Storyboard storyboard, FrameworkElement target, Double? _fromX, Double? _fromY, Double? _toX, Double? _toY, TimeSpan beginTime, TimeSpan _duration, EasingFunctionBase _easingMode)
        {
            storyboard.AddDoubleAnimation(target, TargetProperty.RenderTransformTranslateX, _fromX, _toX, beginTime, _duration, _easingMode);
            storyboard.AddDoubleAnimation(target, TargetProperty.RenderTransformTranslateY, _fromY, _toY, beginTime, _duration, _easingMode);
        }

        public static void AddDoubleAnimationTranslateX(this Storyboard storyboard, FrameworkElement target, Double? _fromX, Double _toX, TimeSpan? beginTime, TimeSpan _duration, EasingFunctionBase _easingMode)
        {
            storyboard.AddDoubleAnimation(target, TargetProperty.RenderTransformTranslateX, _fromX, _toX, beginTime, _duration, _easingMode);
        }

        public static void AddDoubleAnimationTranslateY(this Storyboard storyboard, FrameworkElement target, Double? _fromY, Double _toY, TimeSpan? beginTime, TimeSpan _duration, EasingFunctionBase _easingMode)
        {
            storyboard.AddDoubleAnimation(target, TargetProperty.RenderTransformTranslateY, _fromY, _toY, beginTime, _duration, _easingMode);
        }

        /// <summary>
        /// Starts a Render Transform animation on the specified FrameworkElement
        /// </summary>
        /// <param name="_target">FrameworkElement to animation</param>
        /// <param name="_tX">Ending X offset of animation.</param>
        /// <param name="_tY">Ending Y offset of animation.</param>
        /// <param name="_duration">Duration of the animation</param>
        public static void BeginTranslate(this FrameworkElement _target, Double _tX, Double _tY, TimeSpan _duration)
        {
            Translate(_target, null, null, _tX, _tY, _duration, null, null).Begin();
        }

        /// <summary>
        /// Starts a Render Transform animation on the specified FrameworkElement
        /// </summary>
        /// <param name="_target">FrameworkElement to animation</param>
        /// <param name="_tX">Ending X offset of animation.</param>
        /// <param name="_tY">Ending Y offset of animation.</param>
        /// <param name="_duration">Duration of the animation</param>
        /// <param name="_easingMode">Easing mode to apply to animation. Set as null for none.</param>
        public static void BeginTranslate(this FrameworkElement _target, Double _tX, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode)
        {
            Translate(_target, null, null, _tX, _tY, _duration, _easingMode, null).Begin();
        }

        /// <summary>
        /// Starts a Render Transform animation on the specified FrameworkElement
        /// </summary>
        /// <param name="_target">FrameworkElement to animation</param>
        /// <param name="_fX">Start X offset of animation. Set as null to use current.</param>
        /// <param name="_fY">Start X offset of animation. Set as null to use current.</param>
        /// <param name="_tX">Ending X offset of animation.</param>
        /// <param name="_tY">Ending Y offset of animation.</param>
        /// <param name="_duration">Duration of the animation</param>
        /// <param name="_easingMode">Easing mode to apply to animation. Set as null for none.</param>
        /// <param name="_options">Additional advanced Storyboard options. Set to null for none. (AutoReverse, Repeat, etc.)</param>
        public static void BeginTranslate(this FrameworkElement _target, Double? _fX, Double? _fY, Double _tX, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            (Translate(_target, _fX, _fY, _tX, _tY, _duration, _easingMode, _options)).Begin();
        }

        /// <summary>
        /// Returns a Storyboard that carries out a RenderTransform animation on the Specified FrameworkElement
        /// </summary>
        /// <param name="_target">FrameworkElement to animation</param>
        /// <param name="_fX">Start X offset of animation. Set as null to use current.</param>
        /// <param name="_fY">Start X offset of animation. Set as null to use current.</param>
        /// <param name="_tX">Ending X offset of animation.</param>
        /// <param name="_tY">Ending Y offset of animation.</param>
        /// <param name="_duration">Duration of the animation</param>
        /// <param name="_easingMode">Easing mode to apply to animation. Set as null for none.</param>
        /// <param name="_options">Additional advanced Storyboard options. Set to null for none. (AutoReverse, Repeat, etc.)</param>
        /// <returns>Completed Storyboard</returns>
        public static Storyboard Translate(FrameworkElement _target, Double? _fX, Double? _fY, Double _tX, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                               // Create a storyboard
            String ID = Base.GenerateUniqueID() + "TranslateAnimation";                                     // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID, sb);                                                                  // Register Storyboard with parent element

            sb.AddDoubleAnimation(_target, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)", _fX, _tX, null, _duration, _easingMode);
            sb.AddDoubleAnimation(_target, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)", _fY, _tY, null, _duration, _easingMode);

            // FINALIZE STORYBOARD
            Base.ApplyStoryboardOptions(sb, _options);                                                      // Apply storyboard options
            Base.Register(_target, sb, ID);

            return sb;
        }

        public static void BeginTranslateX(this FrameworkElement _target, Double? _fromX, Double? _toX, TimeSpan _duration, EasingFunctionBase _easing, StoryboardOptions _options)
        {
            (TranslateX(_target, _fromX, _toX, _duration, _easing, _options)).Begin();
        }

        public static Storyboard TranslateX(FrameworkElement _target, Double? _fromX, Double? _toX, TimeSpan _duration, EasingFunctionBase _easing, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                               // Create a storyboard
            String ID = Base.GenerateUniqueID() + "TranslateAnimation";                                     // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID, sb);                                                                  // Register Storyboard with parent element

            sb.AddDoubleAnimation(_target, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)", _fromX, _toX, null, _duration, _easing);

            // FINALIZE STORYBOARD
            Base.ApplyStoryboardOptions(sb, _options);                                                      // Apply storyboard options
            Base.Register(_target, sb, ID);

            return sb;
        }

        public static void BeginTranslateY(this FrameworkElement _target, Double? _fromY, Double? _toY, TimeSpan _duration, EasingFunctionBase _easing, StoryboardOptions _options)
        {
            (TranslateY(_target, _fromY, _toY, _duration, _easing, _options)).Begin();
        }

        public static Storyboard TranslateY(FrameworkElement _target, Double? _fromY, Double? _toY, TimeSpan _duration, EasingFunctionBase _easing, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                               // Create a storyboard
            String ID = Base.GenerateUniqueID() + "TranslateAnimation";                                     // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID, sb);                                                                  // Register Storyboard with parent element

            sb.AddDoubleAnimation(_target, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)", _fromY, _toY, null, _duration, _easing);

            // FINALIZE STORYBOARD
            Base.ApplyStoryboardOptions(sb, _options);                                                     
            Base.Register(_target, sb, ID);
            return sb;
        }

        #endregion

        #region ROTATE ANIMATIONS

        public static void BeginRotate(this FrameworkElement _target, Double? _fX, Double _tX, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Rotate(_target, _fX, _tX, _duration, _easingMode, _options).Begin();
        }

        public static Storyboard Rotate(FrameworkElement _target, Double? _fX, Double _tX, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                               // Create a storyboard
            //FrameworkElement parent = _target.Parent as FrameworkElement;                                   // Get parent element
            String ID = Base.GenerateUniqueID();                                                            // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID + "RotateAnimation", sb);                                            // Register Storyboard with parent element
            _target.GetCompositeTransform();                                                           // Ensure valid composite transform exists on target

            // CREATE X AXIS ANIMATION
            DoubleAnimation ttX = new DoubleAnimation
            {
                From = _fX,
                To = _tX,
                Duration = new Duration(_duration),
                RepeatBehavior = new RepeatBehavior { Count = 1 },
                EasingFunction = _easingMode
            };

            Storyboard.SetTarget(ttX, _target);
            Storyboard.SetTargetName(ttX, _target.Name);
            Storyboard.SetTargetProperty(ttX, TargetProperty.RenderTransformRotation);
            sb.Children.Add(ttX);


            // FINALIZE STORYBOARD
            sb.Duration = _duration;
            Base.ApplyStoryboardOptions(sb, _options);                                                      // Apply storyboard options
            //sb.Completed += (s, e) => { sb = null; _target.Resources.Remove(ID + "RotateAnimation"); };   // Hook unregister event

            return sb;
        }

        #endregion

        #region SCALE ANIMATIONS

        public static void AddDoubleAnimationScale(this Storyboard storyboard, FrameworkElement target, Double? _fX, Double? _fY, Double _tX, Double _tY, TimeSpan? beginTime, TimeSpan _duration, EasingFunctionBase _easingMode)
        {
            storyboard.AddDoubleAnimation(target, TargetProperty.RenderTransformScaleX, _fX, _tX, beginTime, _duration, _easingMode);
            storyboard.AddDoubleAnimation(target, TargetProperty.RenderTransformScaleY, _fY, _tY, beginTime, _duration, _easingMode);
        }

        public static void AddDoubleAnimationScaleX(this Storyboard storyboard, FrameworkElement target, Double? _fX, Double _tX, TimeSpan? beginTime, TimeSpan _duration, EasingFunctionBase _easingMode)
        {
            storyboard.AddDoubleAnimation(target, TargetProperty.RenderTransformScaleX, _fX, _tX, null, _duration, _easingMode);
        }

        public static void AddDoubleAnimationScaleY(this Storyboard storyboard, FrameworkElement target, Double? _fY, Double _tY, TimeSpan? beginTime, TimeSpan _duration, EasingFunctionBase _easingMode)
        {
            storyboard.AddDoubleAnimation(target, TargetProperty.RenderTransformScaleY, _fY, _tY, null, _duration, _easingMode);
        }

        public static void BeginScaleX(this FrameworkElement _target, Double? _fX, Double _tX, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            ScaleX(_target, _fX, _tX, _duration, _easingMode, _options).Begin();
        }

        public static Storyboard ScaleX(FrameworkElement _target, Double? _fX, Double _tX, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                                  // Create a storyboard
            String ID = Base.GenerateUniqueID() + "ScaleAnimation" ;                                           // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID, sb);                                                                     // Register Storyboard with parent element

            sb.AddDoubleAnimationScaleX(_target, _fX, _tX, null, _duration, _easingMode);

            // FINALIZE STORYBOARD
            Base.ApplyStoryboardOptions(sb, _options);                                                      // Apply storyboard options            
            Base.Register(_target, sb, ID);
            return sb;
        }

        


        public static void BeginScaleY(this FrameworkElement _target, Double? _fY, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            ScaleY(_target, _fY, _tY, _duration, _easingMode, _options).Begin();
        }

        public static Storyboard ScaleY(FrameworkElement _target, Double? _fY, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                               // Create a storyboard
            String ID = Base.GenerateUniqueID() + "ScaleAnimation";                                         // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID , sb);                                                                 // Register Storyboard with parent element

            sb.AddDoubleAnimationScaleY(_target, _fY, _tY, null, _duration, _easingMode);

            // FINALIZE STORYBOARD
            Base.ApplyStoryboardOptions(sb, _options);                                                              // Apply storyboard options
            Base.Register(_target, sb, ID);

            return sb;
        }


        public static void BeginScale(this FrameworkElement _target, Double? _fX, Double? _fY, Double _tX, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Scale(_target, _fX, _fY, _tX, _tY, _duration, _easingMode, _options).Begin();
        }

        /// <summary>
        /// Begins a Storyboard that carries out a Scale animation on the Specified FrameworkElement
        /// </summary>
        /// <param name="_target">FrameworkElement to animation</param>
        /// <param name="_fX">Start X scale of animation. Set as null to use current.</param>
        /// <param name="_fY">Start X scale of animation. Set as null to use current.</param>
        /// <param name="_tX">Ending X scale of animation.</param>
        /// <param name="_tY">Ending Y scale of animation.</param>
        /// <param name="_duration">Duration of the animation</param>
        /// <param name="_easingMode">Easing mode to apply to animation. Set as null for none.</param>
        /// <param name="_options">Additional advanced Storyboard options. Set to null for none. (AutoReverse, Repeat, etc.)</param>
        public static Storyboard Scale(FrameworkElement _target, Double? _fX, Double? _fY, Double _tX, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                            // Create a storyboard
            String ID = Base.GenerateUniqueID() + "ScaleAnimation";                                      // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID, sb);                                                               // Register Storyboard with parent element

            sb.AddDoubleAnimationScaleX(_target, _fX, _tX, null, _duration, _easingMode);
            sb.AddDoubleAnimationScaleY(_target, _fY, _tY, null, _duration, _easingMode);

            // FINALIZE STORYBOARD
            Base.ApplyStoryboardOptions(sb, _options);                                                      // Apply storyboard options
            Base.Register(_target, sb, ID);
            return sb;
        }

        #endregion

        #region SKEW ANIMATIONS

        public static void BeginSkewX(this FrameworkElement _target, Double? _fX, Double _tX, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            SkewX(_target, _fX, _tX, _duration, _easingMode, _options).Begin();
        }

        public static void BeginSkewY(this FrameworkElement _target, Double? _fY, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            SkewY(_target, _fY, _tY, _duration, _easingMode, _options).Begin();
        }

        public static void BeginSkew(this FrameworkElement _target, Double? _fX, Double? _fY, Double _tX, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Skew(_target, _fX, _fY, _tX, _tY, _duration, _easingMode, _options).Begin();
        }

        public static void AddDoubleAnimationSkewX(this Storyboard storyboard, FrameworkElement target, Double? _fX, Double _tX, TimeSpan? beginTime, TimeSpan _duration, EasingFunctionBase _easingMode)
        {
            storyboard.AddDoubleAnimation(target, "(UIElement.RenderTransform).(CompositeTransform.SkewX)", _fX, _tX, null, _duration, _easingMode);
        }

        public static void AddDoubleAnimationSkewY(this Storyboard storyboard, FrameworkElement target, Double? _fX, Double _tX, TimeSpan? beginTime, TimeSpan _duration, EasingFunctionBase _easingMode)
        {
            storyboard.AddDoubleAnimation(target, "(UIElement.RenderTransform).(CompositeTransform.SkewY)", _fX, _tX, null, _duration, _easingMode);
        }

        public static Storyboard SkewX(FrameworkElement _target, Double? _fX, Double _tX, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                               // Create a storyboard
            String ID = Base.GenerateUniqueID() + "SkewAnimation";                                          // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID, sb);                                                                  // Register Storyboard with parent element

            sb.AddDoubleAnimationSkewX(_target, _fX, _tX, null, _duration, _easingMode);

            // FINALIZE STORYBOARD
            Base.ApplyStoryboardOptions(sb, _options);
            Base.Register(_target, sb, ID);
            return sb;
        }

        public static Storyboard SkewY(FrameworkElement _target, Double? _fY, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                               // Create a storyboard
            String ID = Base.GenerateUniqueID() + "SkewAnimation";                                          // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID, sb);                                                                  // Register Storyboard with parent element

            sb.AddDoubleAnimationSkewY(_target, _fY, _tY, null, _duration, _easingMode);

            // FINALIZE STORYBOARD
            Base.ApplyStoryboardOptions(sb, _options);                                                      // Apply storyboard options
            Base.Register(_target, sb, ID);

            return sb;
        }

        /// <summary>
        /// Begins a Storyboard that carries out a Skew animation on the Specified FrameworkElement
        /// </summary>
        /// <param name="_target">FrameworkElement to animation</param>
        /// <param name="_fX">Start X Skew of animation. Set as null to use current.</param>
        /// <param name="_fY">Start X Skew of animation. Set as null to use current.</param>
        /// <param name="_tX">Ending X Skew of animation.</param>
        /// <param name="_tY">Ending Y Skew of animation.</param>
        /// <param name="_duration">Duration of the animation</param>
        /// <param name="_easingMode">Easing mode to apply to animation. Set as null for none.</param>
        /// <param name="_options">Additional advanced Storyboard options. Set to null for none. (AutoReverse, Repeat, etc.)</param>
        public static Storyboard Skew(FrameworkElement _target, Double? _fX, Double? _fY, Double _tX, Double _tY, TimeSpan _duration, EasingFunctionBase _easingMode, StoryboardOptions _options)
        {
            Storyboard sb = new Storyboard();                                                               // Create a storyboard
            String ID = Base.GenerateUniqueID() + "SkewAnimation";                                          // Generate a Unique ID for the storyboard
            _target.Resources.Add(ID, sb);                                                                  // Register Storyboard with parent element

            sb.AddDoubleAnimationSkewX(_target, _fX, _tX, null, _duration, _easingMode);
            sb.AddDoubleAnimationSkewY(_target, _fY, _tY, null, _duration, _easingMode);

            // FINALIZE STORYBOARD
            Base.ApplyStoryboardOptions(sb, _options);                                                      // Apply storyboard options
            Base.Register(_target, sb, ID);
            return sb;
        }

        #endregion
    }
}
